Содержание статьи
Шифрование дисков в Linux реализуется с помощью подсистемы dm-crypt Crypto API (встроено в ядро начиная с версии 2.6). Подсистема dm-crypt работает благодаря модулю ядра, который отображает шифрованный диск в виртуальное устройство. На вид оно ничем не отличается от обычного блочного устройства хранения данных. Для управления ключами шифрования используется раздел LUKS (Linux Unified Key Setup). Формат LUKS позволяет использовать до восьми ключей шифрования для одного раздела.
Как устроен раздел LUKS
Раздел LUKS имеет следующий формат.

Он начинается с заголовка phdr, далее за ним следуют слоты с ключевыми данными (KM1, KM2, ..., KM8). За ключевыми данными располагаются данные, шифрованные мастер‑ключом.
Заголовок phdr хранит информацию о протоколе и режиме шифрования, длину ключей, идентификатор UUID и контрольную сумму мастер‑ключа.
В LUKS для одного зашифрованного раздела зарезервировано восемь слотов, в каждом из которых может храниться отдельный ключ. Любой из восьми ключей может быть использован для расшифровки раздела.
Заголовок и слоты ключей можно хранить на другом физическом носителе, отдельно от зашифрованных данных, тем самым реализуя многофакторную защиту. Но при утрате заголовка или слотов получить доступ к зашифрованным данным становится невозможно.
Для управления шифрованием дисков используется утилита cryptsetup.
С помощью этой утилиты возможно:
- создавать шифрованные разделы LUKS;
- открывать и закрывать разделы LUKS;
- управлять слотами ключей;
- дампить заголовок LUKS и мастер‑ключ.
Для работы cryptsetup требуются права суперпользователя и пароль шифрования.
Шифруем
Переходим к практике! Шифровать мы будем только рутовый раздел. Есть, конечно, экзотические кейсы, когда, помимо раздела root, шифруется еще и раздел boot, но такая конфигурация поддерживается не всеми загрузчиками и не считается стандартной и рекомендованной. Мы же вообще не будем использовать загрузчик, так что оставляем boot в покое и шифруем все остальное.
Начнем собирать нашу систему, загрузившись с установочного диска.
Для начала разметим диск следующим образом: sda1 для /, sda2 для /. Схему разделов выбираем GPT.
$ $ $
Далее создадим наш LUKS на sda2.
$
WARNING!========
This will overwrite data on /dev/sda2 irrevocably.
Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for /dev/sda2:
Verify passphrase:
cryptsetup luksFormat /dev/sda2 18.16s user 1.88s system 80% cpu 24.742 total
Взглянем теперь на LUKS.
$
LUKS header information
Version: 2
Epoch: 3
Metadata area: 16384 [bytes]
Keyslots area: 16744448 [bytes]
UUID: e04b5b87-6bfc-4f73-83b0-36f91d52f141
Label: (no label)
Subsystem: (no subsystem)
Flags: (no flags)
Data segments:
0: crypt
offset: 16777216 [bytes]
length: (whole device)
cipher: aes-xts-plain64
sector: 512 [bytes]
Keyslots:
0: luks2
Key: 512 bits
Priority: normal
Cipher: aes-xts-plain64
Cipher key: 512 bits
PBKDF: argon2i
Time cost: 7
Memory: 483194
Threads: 2
Salt: a8 d7 82 ce 89 c8 0f d6 29 18 83 e5 5d 9d a7 f1
a2 6d 66 81 70 db c4 82 cc fb ae 81 4c 7f ed 0c
AF stripes: 4000
AF hash: sha256
Area offset:32768 [bytes]
Area length:258048 [bytes]
Digest ID: 0
Tokens:
Digests:
0: pbkdf2
Hash: sha256
Iterations: 149967
Salt: 87 ac 6f 61 75 fb 91 14 63 5d ca 5d 1c 25 ef 42
7b af 51 63 34 eb 26 d5 d7 be 7a 78 7b 2a 25 f1
Digest: da e7 fd 26 59 85 5d 5e 34 79 2a fa 20 95 f1 83
13 10 0d 0e a3 58 a6 0e 33 b0 f0 73 e8 0a a1 1e
Видно, что мы задействовали один из восьми доступных слотов с ключами — доступ к нему ограничен паролем.
Алгоритм шифрования по умолчанию (aes-xts-plain64) нас устроит. Перечень всех поддерживаемых алгоритмов зависит от ядра (загляни в /). Можно протестировать работу с ними.
$ #
PBKDF2-sha1 1817289 iterations per second for 256-bit key
PBKDF2-sha256 2302032 iterations per second for 256-bit key
PBKDF2-sha512 1646116 iterations per second for 256-bit key
PBKDF2-ripemd160 903944 iterations per second for 256-bit key
PBKDF2-whirlpool 681778 iterations per second for 256-bit key
argon2i N/A
argon2id N/A#
aes-cbc 128b 1126.9 MiB/s 2966.7 MiB/s
serpent-cbc 128b 98.6 MiB/s 736.0 MiB/s
twofish-cbc 128b 226.0 MiB/s 404.9 MiB/s
aes-cbc 256b 868.5 MiB/s 2594.2 MiB/s
serpent-cbc 256b 107.9 MiB/s 779.9 MiB/s
twofish-cbc 256b 241.6 MiB/s 433.4 MiB/s
aes-xts 256b 3152.7 MiB/s 3146.5 MiB/s
serpent-xts 256b 676.5 MiB/s 665.9 MiB/s
twofish-xts 256b 400.6 MiB/s 410.4 MiB/s
aes-xts 512b 2614.9 MiB/s 2600.2 MiB/s
serpent-xts 512b 687.1 MiB/s 711.3 MiB/s
twofish-xts 512b 413.0 MiB/s 417.2 MiB/s
cryptsetup benchmark 9.54s user 23.98s system 107% cpu 31.226 total
Откроем раздел.
$
Enter passphrase for /dev/sda2:
После этих манипуляций у нас в системе стал доступен новый раздел cryptroot.
$
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 8G 0 disk├─sda1 └─sda2 └─cryptroot
Создадим на разделах файловые системы.
$ mkfs.fat -F32 /dev/sda1
$ mkfs.btrfs -f -L "archroot" /dev/mapper/cryptroot
Для продолжения установки примонтируем наши разделы.
$ mount /dev/mapper/cryptroot /mnt
$ btrfs subvolume create /mnt/@root
$ btrfs subvolume create /mnt/@home
$ umount /mnt
$ mount /dev/mapper/cryptroot /mnt -o subvol=@root,ssd,noatime,space_cache,compress=zstd
$ mkdir /mnt/home
$ mount /dev/mapper/cryptroot /mnt/home -o subvol=@home,ssd,noatime,space_cache,compress=zstd
$ mkdir /mnt/boot
$ mount /dev/sda1 /mnt/boot
Заглядывая одним глазом в вики, устанавливаем систему обычным образом.
Не забудем включить хук encrypt при создании initramfs.
$ vim /etc/mkinitcpio.conf
HOOKS=(base udev autodetect keyboard keymap modconf block encrypt filesystems)$ mkinitcpio -p linux
Финальный этап — настройка UEFI для загрузки нашего ядра. Напоминаю, что мы обойдемся без GRUB и будем загружать ядро напрямую, используя фичу EFISTUB.
Добавляем запись в UEFI.
$ efibootmgr \--create \--bootnum=0000 \--label "Arch Linux" \--disk /dev/sda \--part 1 \--loader /vmlinuz-linux \--unicode 'cryptdevice=/dev/sda2:cryptroot root=/dev/mapper/cryptroot rootflags=subvol=@root rw initrd=\initramfs-linux.img' \--verboseОсобое внимание обрати на параметры ядра! Без них система просто не сможет найти шифрованный раздел.
Перезагружаемся и убеждаемся, что все работает, как мы хотели: на этапе загрузки требуется ввод пароля для расшифрования раздела.

Настраиваем авторазблокировку
Хотелось бы не вводить пароль при загрузке каждый раз. Для этого нужно настроить авторазблокировку LUKS с помощью чипа TPM.
Убедимся, что мы счастливые обладатели TPM. Необходимая версия TPM — 2.0.
$
PASS$
TPM 2.0 Device
or$
2
Авторазблокировку сделаем с помощью относительно новой фичи systemd-cryptsetup.
info
Systemd-cryptsetup добавлена в systemd начиная с версии 248.
Для начала необходимо проверить нашу версию systemd.
$
systemd 249 (249.2-1-arch)+PAM +IPTC
Все отлично. Для работы с TPM нужно дополнительно установить пакет tpm2-tss.
$ pacman -Sy tpm2-tss
Далее добавим ключ TPM в слот LUKS с помощью тулзы systemd-cryptenroll.
$ systemd-cryptenroll /dev/sda2 --tpm2-device=auto --tpm2-pcrs=0,4
Не забываем проверить.
$
SLOT TYPE---------
0 password
1 tpm2
В команде выше мы указали параметр --tpm2-pcrs=0,. Что такое PCR? Вот определение из Википедии:
PCR — это внутренние регистры памяти TPM, в которых в зашифрованном виде содержится вся информация о целостности метрик системы, начиная с загрузки BIOS до завершения работы системы. Информация, содержащаяся в PCR, формирует корень доверия для измерений (RTM). Могут храниться как в энергонезависимой, так и в энергозависимой памяти. Эти регистры сбрасываются при старте и при перезагрузке системы. Спецификация предписывает минимальное количество регистров (16), каждый регистр содержит 160 бит информации. Регистры 0–7 зарезервированы для нужд TPM. Регистры 8–15 доступны для использования операционной системой и приложениями. Изменения значений PCR необратимы, и их значения нельзя записать напрямую, их можно только расширить новыми значениями, которые зависят от предыдущих. Все изменения значений PCR записываются в лог изменений, который хранится в энергозависимой памяти.
| PCR | Use |
|---|---|
| PCR0 | Core System Firmware executable code (aka Firmware) |
| PCR1 | Core System Firmware data (aka UEFI settings) |
| PCR2 | Extended or pluggable executable code |
| PCR3 | Extended or pluggable firmware data |
| PCR4 | Boot Manager |
| PCR5 | GPT / Partition Table |
| PCR6 | Resume from S4 and S5 Power State Events |
| PCR7 | Secure Boot State |
| PCR8 | Hash of the booted kernel |
| PCR 9 to 10 | Reserved for Future Use |
| PCR11 | BitLocker Access Control |
| PCR12 | Data events and highly volatile events |
| PCR13 | Boot Module Details |
| PCR14 | Boot Authorities |
| PCR 15 to 23 | Reserved for Future Use |
Для включения systemd-cryptsetup необходимо добавить в / хуки systemd и sd-encrypt.
HOOKS=(base systemd modconf block keyboard sd-encrypt filesystems fsck)$ mkinicpio -PНаконец, изменим строку загрузки, добавив параметры rd. и rd..
$
/dev/sda2: UUID="e04b5b87-6bfc-4f73-83b0-36f91d52f141" TYPE="crypto_LUKS" PARTLABEL="primary" PARTUUID="136ffe26-5569-4c76-a871-9025ea52dbd8"
$ --create --bootnum=0001 --label --disk /--part --loader /--unicode --verbose
Перезагружаемся и проверяем, что все работает как надо.
Для тех, кому systemd-cryptsetup по каким‑то причинам не подходит (например, версия systemd старая или вообще нет systemd), могу порекомендовать фреймворк Clevis. С его помощью можно реализовать следующие сценарии авторазблокировки LUKS:
-
tpm2— разблокировка с TPM; -
tang— разблокировка с сетевого сервера.
Вот как использовать Сlevis с TPM:
$
Enter existing LUKS password: ***************
Проверяем.
$
1: tpm2 '{"hash":"sha256","key":"ecc","pcr_bank":"sha1","pcr_ids":"0,4"}
Вариант с сервером tang настраивается не сложнее (разумеется, заранее разворачиваем tang и обеспечиваем сетевую доступность):
$
The advertisement contains the following signing keys:
QjvwIdUeor3gBqxLteIOKvWPRqM
Do
Enter existing LUKS password:
Проверяем.
$ 1: 2:
В заключение темы авторазблокировки стоит упомянуть некоторые ограничения фреймворка Clevis:
- Clevis работает только с TPM 2.0; TPM 1.2 не поддерживается.
- В Tang не реализован механизм смены ключей.
- Для Tang необходима доступность сети на этапе загрузки, поэтому, например, для Wi-Fi-подключения требуется дополнительная настройка адаптера в initrd.
Создание ключей восстановления
Бэкапы ключей в таком деле, как шифрование, должны быть, об этом знает каждый.
Есть два варианта создания ключей восстановления:
- дамп заголовка и мастер‑ключа;
- запись пароля восстановления в Active Directory.
Делаем копию мастер-ключа
Сделать резервную копию заголовка и мастер‑ключа можно с помощью хорошо известной нам утилиты cryptsetup.
warning
С помощью созданной копии можно будет получить доступ к разделу, не зная пароль, поэтому нас заботливо предупреждают, что хранить ее нужно в секрете!
$
WARNING!========
The header dump with volume key is sensitive information
that allows access to encrypted partition without a passphrase.
This dump should be stored encrypted in a safe place.
Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for /dev/sda2:
LUKS header information for /dev/sda2
Cipher name: aes
Cipher mode: xts-plain64
Payload offset: 32768
UUID: e04b5b87-6bfc-4f73-83b0-36f91d52f141
MK bits: 512
Key stored to file mk.dump
$
00000000: 1501 eccc 84fd 82f7 f53b 56ae 4ab8 5775 .........;V.J.Wu
00000010: 4968 ea61 8d01 1f29 985e 33af 5537 ba40 Ih.a...).3.U7.@
00000020: af61 6aa2 b384 afe1 aff6 d4c9 ad95 1d67 .aj............g
00000030: 3a4f d64f 33ea 6f50 0a18 1139 9196 d694 :O.O3.oP...9....
При необходимости можно восстановить заголовок и установить новый пароль.
$
Enter new passphrase for key slot: ********
Verify passphrase: ********
Добавляем пароль восстановления в Active Directory
Другой интересный способ — это хранение пароля в AD.
Для начала нужно включить нашу Arch Linux в домен. Делать это буду с помощью System Security Services Daemon (SSSD) и realmd.
Устанавливаем sssd обычным способом:
$ pacman -Sy sssd
А realmd ставим из AUR.
Убедимся, что домен доступен.
$
ad.localdomain
type: kerberos
realm-name: AD.LOCALDOMAIN
domain-name: ad.localdomain
configured: no
server-software: active-directory
client-software: sssd
Присоединяемся к домену, для этого понадобится учетная запись с правами ввода в домен (для простоты — administrator).
$ realm join ad.localdomain
Password for Administrator: ********Проверяем, что все прошло успешно и мы получили билеты Kerberos:
$
ad.localdomain
type: kerberos
realm-name: AD.LOCALDOMAIN
domain-name: ad.localdomain
configured: kerberos-member
server-software: active-directory
client-software: sssd
login-formats: %U@ad.localdomain
login-policy: allow-realm-logins$
Keytab name: FILE:/etc/krb5.keytab
KVNO Principal----
2 ARCH$@AD.LOCALDOMAIN
2 ARCH$@AD.LOCALDOMAIN
2 ARCH$@AD.LOCALDOMAIN
2 host/ARCH@AD.LOCALDOMAIN
2 host/ARCH@AD.LOCALDOMAIN
2 host/ARCH@AD.LOCALDOMAIN
2 RestrictedKrbHost/ARCH@AD.LOCALDOMAIN
2 RestrictedKrbHost/ARCH@AD.LOCALDOMAIN
2 RestrictedKrbHost/ARCH@AD.LOCALDOMAIN
Пароль восстановления будем хранить в учетной записи компьютера: это дочерний объект класса msFVE-RecoveryInformation. Так делает виндовый BitLocker, аналогично поступим и мы.
Записывать пароль в AD буду с помощью скрипта на Python.
$ git clone https://github.com/ambalabanov/lukscrow
$ cd lukscrow/
$ pip install -r requirements.txt
Запускаем утилиту, указав необходимые параметры и текущий ключ (пароль):
$ ./ ********Using Connected
Generating recovery key...
Adding recovery key to LUKS volume...
New key added.
Storing recovery key in Active Directory
Recovery key saved to AD.
Remove Kerberos credential cache /tmp/tmpcsdyn49r
Removed cache file.
Проверяем в AD, что пароль восстановления успешно записался.

Ну и напоследок проверяем сам пароль.
$ Enter
TRUE
Заключение
Итак, мы установили Arch Linux на шифрованный корневой раздел, настроили авторазблокировку при старте и позаботились о резервировании ключей. Надеюсь, для тебя это тоже было увлекательным приключением!









